/*
 * 原版接口
 */
import * as server from "@minecraft/server";
/*
 * 蛋白石数据
 */
import * as data_format from "../data/format";
import * as data_entity from "../data/entity";
import * as data_story from "../data/story";
/*
 * 蛋白石组件
 */
import * as power_property from "./power_property";
import * as native_tools from "./native_tools";
import * as math_tools from "./math_tools";
/*
 * 导出模块
 */
export {
    PLAN,
    Control,
    Template,
    ROUTE_ARGS,
    PathExecute,
    AFTER_PLAN_DATA,
    MistySeaFissure,
    RunePowerAttack,
    RuneClingEntity,
    ROUTE_MOVE_CODE,
    ROUTE_CLOSE_CODE,
    BEFORE_PLAN_DATA,
    ControlEventTrigger,
    SprayParticleTrigger,
};
/**
 * * 运行并显示 计划表 的 反馈信息
 */
class Control {
    /**
     * * 持续性 的 计划表 队列
     */
    public static inventory: PLAN[] = [];
    /**
     * * 是否显示错误信息
     */
    public static canDisplayError: boolean = false;
    /**
     * * 执行 全部计划表
     */
    public static execute() {
        // 验证 计划表队列 是否为空
        if (this.inventory.length == 0) return;
        /**
         * * 等待移除 的 计划表 的 唯一识别符
         */
        const waitRemove = new Set<number>();
        // 执行 全部 计划表
        this.inventory.forEach(
            plan => {
                // 运行 时间刻积分
                plan.timeScore += 1;
                // 验证 时间刻积分 是否满足 执行条件
                if (plan.timeScore % plan.cooldown != 0) return;
                /**
                 * * 设定 是否运行当前实例
                 */
                let license: boolean = true;
                /**
                 * * 将 计划表 标记为 等待移除
                 */
                const remove = () => waitRemove.add(plan.numberId);
                /**
                 * * 定义 当前实例 是否暂停运行
                 */
                const cease = () => license = false;
                // 运行 计划表
                try {
                    // 运行 计划表 前处理 回调函数
                    plan.beforePlanEvent({ plan, remove, cease });
                    // 运行 计划表 后处理 回调函数
                    if (license) plan.afterPlanEvent({ plan, remove });
                }
                catch (error) {
                    if (!(error instanceof Error)) return;
                    /**
                     * * 错误路径
                     */
                    const stack = error.stack ?? './';
                    /**
                     * * 错误描述
                     */
                    const message = JSON.stringify(error.message);
                    /**
                     * * 截取报错信息
                     */
                    const intel = 'THE_OTHER_SHORE:' + message + stack;
                    // 输出 错误信息
                    if (this.canDisplayError) console.error(intel);
                    // 将 计划表 标记为 等待移除
                    remove();
                };
            }
        )
        // 移除 被标记为 等待移除 的 计划表
        if (waitRemove.size > 0) this.inventory = this.inventory.filter(plan => !waitRemove.has(plan.numberId));
    };
    /**
     * * 私有构造函数
     */
    protected constructor() { };
};
/**
 * * 计划表 模板类
 */
class Template implements PLAN {
    /**
     * * 当前计划表 的 数字标识符
     */
    public numberId: number;
    /** 
     * * 实例 的 时间刻积分
     */
    public timeScore = 1;
    /**
     * * 计划表 前处理 回调函数
     */
    public beforePlanEvent(data: BEFORE_PLAN_DATA): void { };
    /**
     * * 计划表 后处理 回调函数
     */
    public afterPlanEvent(data: AFTER_PLAN_DATA): void { };
    /**
     * @param {string} className - 类名
     * 
     * @param {number} cooldown - 重复执行的间隔时间
     * 
     * @param {ANNEX_ARGS} annex - 附加参数
     */
    protected constructor(public className: string, public cooldown: number, public annex: ANNEX_ARGS) {
        this.cooldown = Math.floor(cooldown);
        this.numberId = math_tools.NumberID();
    };
    /**
     * * 创建 计划表 并 返回 计划表对象
     * 
     * @param {string} className - 类名
     * 
     * @param {number} cooldown - 冷却时间
     * 
     * @param {ANNEX_ARGS} data - 附加参数
     */
    static Create(className: string, cooldown: number, data: ANNEX_ARGS): PLAN {
        // 验证类名
        if (className == "") throw new Error("类名不能为空");
        /**
         * * 创建 计划表对象
         */
        const plan = new this(className, cooldown, data);
        // 将 计划表 添加到 持久性计划表
        Control.inventory.push(plan);
        // 返回 计划表对象
        return plan;
    };
};
// 运行 计划表
server.system.runInterval(() => Control.execute());
/**
 * * 在路径上执行
 */
class PathExecute extends Template {
    /**
     * * 计划表 的 路径索引
     */
    public pathIndex = 1;
    /**
     * * 计划表 的 当前位置
     */
    public current = math_tools.Vector.CONSTANT_ZERO;
    /**
     * * 当前位置与节点位置间的矢量关系
     */
    public direction = math_tools.Vector.CONSTANT_ZERO;
    /**
     * * 当前位置与节点位置间的距离
     */
    public nodeDistance = 0;
    /**
     * * 计划表 的 路径坐标组
     */
    public location_group: server.Vector3[] = [];
    /**
     * * 运行维度
     */
    public dimension: server.Dimension = server.world.getDimension('overworld');
    /**
     * * 粒子轨迹
     */
    public particle: string[] = [];
    /**
     * * 在运行时执行
     */
    public on_move?: ROUTE_MOVE_CODE;
    /**
     * * 在结束时执行
     */
    public on_done?: ROUTE_CLOSE_CODE;
    /**
     * * 位置偏移
     */
    public offset?: server.Vector3;
    /**
     * * 附加参数
     */
    public annex: ANNEX_ARGS = {};
    /**
     * * 运行速度
     */
    public speed: number = 1;
    /**
     * * 路径执行计划表 抵达 边界点 的 次数
     */
    public boundsCounting: number = 0;
    public afterPlanEvent(data: AFTER_PLAN_DATA) {
        if (this.speed == 1) this.executePathPlan(data);
        else for (let index = 0; index < this.speed; index++) this.executePathPlan(data);
    };
    /**
     * * 执行 路径计划
     */
    public executePathPlan(data: AFTER_PLAN_DATA) {
        // 判断越界次数
        if (this.boundsCounting >= 1) return;
        /**
         * * 当前位置与节点位置间的距离
         */
        const distance = math_tools.Vector.distance(this.location_group[this.pathIndex], this.current);
        /**
         * * 终点位置的索引
         */
        const endIndex = this.location_group.length - 1;
        /**
         * * 判断是否继续运行
         */
        const enable = math_tools.Vector.distance(this.current, this.location_group[endIndex]) < 1;
        /**
         * * 路径计划 附加参数
         */
        const pathAnnexData: data_format.ROUTE_ANNEX_ARGS = {
            dimension: this.dimension,
            location: this.current,
            tick: this.timeScore
        };
        // 判断 是否执行 运行时 的 附加程序
        if (this.on_move) {
            /**
             * * 执行 运行时 的 附加程序
             */
            const onClose = this.on_move(pathAnnexData);
            // 判断 是否执行 关闭事件
            if (onClose == false && this.on_done) this.on_done(pathAnnexData);
            //检测是否需要关闭程序运行
            if (onClose == false) return data.remove();
        };
        // 判断 是否需要显示粒子效果
        if (this.particle.length > 0) this.particle.forEach(particle => native_tools.TrySpawnParticle(this.dimension, particle, this.current));
        // 修改 计划表 指向的位置
        this.modifyCurrentLocation(this, distance, endIndex);
        // 判断 是否继续运行
        if (!enable || this.pathIndex != endIndex) return;
        // 执行结束事件
        if (this.on_done) this.on_done(pathAnnexData);
        // 记录越界次数
        this.boundsCounting += 1;
        // 移除 计划表
        return data.remove();
    };
    /**
     * * 修改 路径执行 当前指向 的 位置
     * 
     * @param {PathExecute} self - 路径执行计划表
     * 
     * @param {number} distance - 当前位置与节点位置间的距离
     * 
     * @param {number} endIndex - 终点位置的索引
     */
    public modifyCurrentLocation(self: PathExecute, distance: number, endIndex: number) {
        //判断 是否执行下一个位置
        if (distance < 1 && self.pathIndex != endIndex) {
            // 获取下一个位置
            self.pathIndex += 1;
            // 获取下一个位置与当前位置之间的矢量
            self.direction = math_tools.Vector.subtract(self.location_group[self.pathIndex], self.current);
            // 获取下一个位置与当前位置之间的矢量距离
            self.nodeDistance = math_tools.Vector.distance(self.location_group[self.pathIndex], self.current);
        }
        // 修改 计划表 指向的位置
        else {
            self.current.x += (self.direction.x / self.nodeDistance);
            self.current.y += (self.direction.y / self.nodeDistance);
            self.current.z += (self.direction.z / self.nodeDistance);
        };
    };
    /**
     * 基于 射击参数 初始化 路径属性
     * 
     * @param {PathExecute} source - 路径执行计划表
     * 
     * @param {ANNEX_ARGS} args - 执行表参数
     * 
     * @param {server.Vector3} offset - 位置偏移
     * 
     * @class {@link PathExecute}
     */
    protected static prepareShootingPath(source: PathExecute, args: ANNEX_ARGS, offset?: server.Vector3) {
        // 验证参数
        if (!source || !args.shoot) return;
        /**
         * * 计算 终点坐标
         */
        const endPoint = math_tools.Vector.add(args.shoot.start_place, math_tools.Vector.scale(args.shoot.toward, args.shoot.max_distance));
        // 重载 坐标组数据
        source.location_group = [args.shoot.start_place, endPoint];
        // 如果有偏移量, 则调整位置组中的坐标
        if (offset) source.location_group = source.location_group.map(location => math_tools.Vector.add(location, offset));
        // 获取当前位置与目标位置之间的矢量距离
        source.current = source.location_group[0];
        source.direction = math_tools.Vector.subtract(source.location_group[1], source.current);
        source.nodeDistance = math_tools.Vector.distance(source.location_group[1], source.current);
    };
    /**
     * 基于 默认参数 初始化 路径属性
     * 
     * @param {PathExecute} source - 路径型代码
     * 
     * @param {server.Vector3} offset - 偏移
     * 
     * @class {@link PathExecute}
     */
    protected static prepareDefaultPath(source: PathExecute, offset?: server.Vector3) {
        /**
         * * 获取 玩家对象
         */
        const player = source.dimension.getPlayers()[0];
        // 根据位置组长度设置新的位置组
        if (source.location_group.length === 0) {
            // 设置起点和终点
            source.location_group = [player.location, math_tools.Vector.add(player.location, { x: 10, y: 10, z: 10 })];
        }
        else if (source.location_group.length === 1) {
            // 只有起点, 添加终点
            source.location_group.push(math_tools.Vector.add(source.location_group[0], { x: 10, y: 10, z: 10 }));
        }
        // 如果有偏移量, 则调整位置组中的坐标
        if (offset) source.location_group = source.location_group.map(location => math_tools.Vector.add(location, offset));
        // 获取当前位置与目标位置之间的矢量距离
        source.current = source.location_group[0];
        source.direction = math_tools.Vector.subtract(source.location_group[1], source.current);
        source.nodeDistance = math_tools.Vector.distance(source.location_group[1], source.current);
    };
    /**
     * * 创建 路径执行计划表 并 返回 计划表对象
     * 
     * @param {string} className - 类名
     * 
     * @param {number} cooldown - 冷却时间
     * 
     * @param {ROUTE_ARGS} data - 路径参数
     */
    static Create(className: string, cooldown: number, data: ROUTE_ARGS): PathExecute {
        // 验证类名
        if (className == "") throw new Error("类名不能为空");
        /**
         * * 创建 计划表对象
         */
        const plan = new PathExecute(className, cooldown, data);
        /**
         * @returns {ROUTE_MOVE_CODE} - 射程参数
         */
        const shootToward = (): ANNEX_ARGS => {
            if (data.shoot) {
                return {
                    shoot: {
                        toward: data.shoot.toward,
                        start_place: data.shoot.start_place,
                        max_distance: Math.floor(data.shoot.max_distance)
                    }
                }
            }
            else return {};
        };
        // 设定 路径与射程参数
        plan.location_group = data.location_group;
        plan.dimension = data.dimension;
        plan.cooldown = Math.floor(data.cooldown);
        plan.particle = data.particles;
        plan.on_move = data.on_move;
        plan.on_done = data.on_done;
        plan.offset = data.offset;
        plan.speed = Math.floor(data.speed);
        plan.annex = shootToward();
        // 尝试基于射击参数进行初始化
        if (data.shoot) PathExecute.prepareShootingPath(plan, data, plan.offset);
        // 尝试基于默认参数进行初始化
        if (!data.shoot) PathExecute.prepareDefaultPath(plan, plan.offset);
        // 将 计划表 添加到 持久性计划表队列
        Control.inventory.push(plan);
        // 返回 计划表对象
        return plan;
    };
    /**
     * * 创建 实心立方体 造型 的 路径计划表 并 返回 计划表对象
     * 
     * @param {string} className - 当前计划表的识别名称
     * 
     * @param {ROUTE_ARGS} data - 路径参数
     * 
     * @param {server.Vector3} start - 路径的起始位置
     * 
     * @param {server.Vector3} done - 路径的结束位置
     * 
     * @param {number|undefined} multiple - 速度的缩放倍率
     * 
     * @returns {PathExecute} - 计划表对象
     */
    static CreateForCube(className: string, data: ROUTE_ARGS, start: server.Vector3, done: server.Vector3, multiple: number = 1): PathExecute {
        // 验证类名
        if (className == "") throw new Error("类名不能为空");
        /**
         * * 创建 计划表对象
         */
        const plan = new PathExecute(className, 1, data);
        // 对偏移量进行处理
        if (plan.offset) start = math_tools.Vector.add(start, plan.offset);
        if (plan.offset) done = math_tools.Vector.add(done, plan.offset);
        /**
         * * 计算 距离
         */
        const distance = math_tools.Vector.distance(done, start);
        /**
         * * 拷贝 起始点 坐标
         */
        const copyStart = math_tools.Vector.copy(start);
        /**
         * * 修改 终止点 坐标
         */
        const copyDone = math_tools.Vector.copy(done);
        // 修改 路径计划表 的 函数
        plan.modifyCurrentLocation = (self: PathExecute) => {
            if (self.current.x != copyDone.x) {
                self.current.x += copyDone.x > self.current.x ? 1 : -1;
            }
            else if (self.current.x == copyDone.x && self.current.z != copyDone.z) {
                self.current.z += copyDone.z > self.current.z ? 1 : -1;
                self.current.x = copyStart.x
            }
            else if (self.current.x == copyDone.x && self.current.z == copyDone.z && self.current.y != copyDone.y) {
                self.current.y += copyDone.y > self.current.y ? 1 : -1;
                self.current.x = copyStart.x
                self.current.z = copyStart.z
            }
            else if (self.current.x == copyDone.x && self.current.z == copyDone.z && self.current.y == copyDone.y) {
                self.pathIndex = 1;
                self.boundsCounting += 2;
            };
        };
        // 修改 路径计划表 的 参数
        plan.nodeDistance = math_tools.Vector.distance(done, start);
        plan.direction = math_tools.Vector.subtract(done, start);
        plan.location_group = [start, done];
        plan.dimension = data.dimension;
        plan.cooldown = Math.floor(data.cooldown);
        plan.particle = data.particles;
        plan.on_move = data.on_move;
        plan.on_done = data.on_done;
        plan.offset = data.offset;
        plan.current = start;
        plan.speed = Math.floor((distance - 1) * multiple) + 1;
        // 将 计划表 添加到 持久性计划表队列
        Control.inventory.push(plan);
        // 返回 计划表对象
        return plan;
    };
    /**
     * * 创建 立方体边框 造型 的 路径计划表 并 返回 计划表对象
     * 
     * @param {string} className - 当前计划表的识别名称
     * 
     * @param {ROUTE_ARGS} data - 路径参数
     * 
     * @param {server.Vector3} start - 路径的起始位置
     * 
     * @param {server.Vector3} done - 路径的结束位置
     * 
     * @returns {PathExecute} - 计划表对象
     */
    static CreateForFrame(className: string, data: ROUTE_ARGS, start: server.Vector3, done: server.Vector3): PathExecute {
        // 验证类名
        if (className == "") throw new Error("类名不能为空");
        /**
         * * 创建 计划表对象
         */
        const plan = new PathExecute(className, 1, data);
        /**
         * * 计算 向量
         */
        const vector = math_tools.Vector.subtract(done, start);
        /**
         * * 计算 距离
         */
        const distance = math_tools.Vector.distance(done, start);
        /**
         * * 路径序列
         */
        const location_group: server.Vector3[] = [];
        // 序列化 3D 结构
        if (vector.y != 0 && vector.x != 0 && vector.z != 0) {
            location_group.push(
                // 绘制 双平面 结构
                math_tools.Vector.add(start, { x: vector.x, y: 0, z: 0 }),
                math_tools.Vector.add(start, { x: vector.x, y: vector.y, z: 0 }),
                math_tools.Vector.add(start, { x: 0, y: vector.y, z: 0 }),
                math_tools.Vector.add(start, math_tools.Vector.CONSTANT_ZERO),
                math_tools.Vector.add(start, { x: 0, y: 0, z: vector.z }),
                math_tools.Vector.add(start, { x: 0, y: vector.y, z: vector.z }),
                math_tools.Vector.add(start, { x: 0, y: vector.y, z: 0 }),
                math_tools.Vector.add(start, math_tools.Vector.CONSTANT_ZERO),
                // 修正 基准点
                math_tools.Vector.add(start, { x: vector.x, y: 0, z: 0 }),
                math_tools.Vector.add(start, { x: vector.x, y: 0, z: vector.z }),
                // 绘制 双平面 结构
                math_tools.Vector.add(start, { x: 0, y: 0, z: vector.z }),
                math_tools.Vector.add(start, { x: 0, y: vector.y, z: vector.z }),
                math_tools.Vector.add(start, { x: vector.x, y: vector.y, z: vector.z }),
                math_tools.Vector.add(start, { x: vector.x, y: 0, z: vector.z }),
                math_tools.Vector.add(start, { x: vector.x, y: 0, z: 0 }),
                math_tools.Vector.add(start, { x: vector.x, y: vector.y, z: 0 }),
                math_tools.Vector.add(start, { x: vector.x, y: vector.y, z: vector.z }),
            )
        }
        else if (vector.y == 0 && vector.x != 0 && vector.z != 0) {
            location_group.push(
                math_tools.Vector.add(start, { x: vector.x, y: 0, z: 0 }),
                math_tools.Vector.add(start, { x: vector.x, y: 0, z: vector.z }),
                math_tools.Vector.add(start, { x: 0, y: 0, z: vector.z }),
                math_tools.Vector.add(start, math_tools.Vector.CONSTANT_ZERO)
            )
        }
        else if (vector.y != 0 && vector.x == 0 && vector.z != 0) {
            location_group.push(
                math_tools.Vector.add(start, { x: 0, y: vector.y, z: 0 }),
                math_tools.Vector.add(start, { x: 0, y: vector.y, z: vector.z }),
                math_tools.Vector.add(start, { x: 0, y: 0, z: vector.z }),
                math_tools.Vector.add(start, math_tools.Vector.CONSTANT_ZERO)
            )
        }
        else if (vector.y != 0 && vector.x != 0 && vector.z == 0) {
            location_group.push(
                math_tools.Vector.add(start, { x: 0, y: vector.y, z: 0 }),
                math_tools.Vector.add(start, { x: vector.x, y: vector.y, z: 0 }),
                math_tools.Vector.add(start, { x: vector.x, y: 0, z: 0 }),
                math_tools.Vector.add(start, math_tools.Vector.CONSTANT_ZERO)
            )
        };
        // 修改 路径规划日程表 的 参数
        plan.location_group = [start, ...location_group];
        plan.dimension = data.dimension;
        plan.cooldown = Math.floor(data.cooldown);
        plan.particle = data.particles;
        plan.on_move = data.on_move;
        plan.on_done = data.on_done;
        plan.offset = data.offset;
        plan.speed = distance;
        // 基于默认参数进行初始化
        PathExecute.prepareDefaultPath(plan, plan.offset);
        // 将 计划表 添加到 持久性计划表队列
        Control.inventory.push(plan);
        // 返回 计划表对象
        return plan;
    };
};
/**
 * * 雾海裂隙
 * 
 * @param {number} cooldown tick间隔时间, 建议值为: 30
 */
class MistySeaFissure extends Template {
    public afterPlanEvent(data: AFTER_PLAN_DATA) {
        // 检测 条件是否齐全
        if (!this.annex.dimension_group) return data.remove();
        if (!this.annex.location_group) return data.remove();;
        /**
         * * 获取 显示维度
         */
        const showDimension = this.annex.dimension_group[0];
        /**
         * * 获取 显示位置
         */
        const showLocation = this.annex.location_group[0];
        /**
         * * 获取 指向位置
         */
        const pointLocation = this.annex.location_group[1];
        /**
         * * 获取 指向维度
         */
        const pointDimension = this.annex.dimension_group[1];
        // 显示 粒子效果
        native_tools.TrySpawnParticle(showDimension, 'constant:the_cracks_of_the_misty_sea', showLocation);
        // 传送 实体
        showDimension.getEntities({ location: showLocation, maxDistance: 3 }).forEach(entity => entity.tryTeleport(pointLocation, { dimension: pointDimension }))
    }
};
/**
 * * 符文攻击
 */
class RunePowerAttack extends Template {
    /**
     * * 获取 伤害参数
     * 
     * @param {server.Entity | server.Player} self - 发起元素攻击的实体
     * 
     * @returns {server.EntityApplyDamageOptions} - 伤害参数
     */
    protected DamageOptions(self: server.Entity | server.Player): server.EntityApplyDamageOptions {
        return {
            cause: server.EntityDamageCause[this.annex.hurt_Type ?? 'entityAttack'],
            damagingEntity: self
        };
    };
    public afterPlanEvent(data: AFTER_PLAN_DATA) {
        /**
         * * 获取 目标实体
         */
        const target = this.annex.target;
        /**
         * * 获取 是否暴击
         */
        const erupt = this.annex.erupt;
        /**
         * * 获取 自身实体
         */
        const self = this.annex.self;
        // 判断 条件是否满足
        if (!self || !target || !target.isValid() || !target.getComponent('health') || erupt == undefined) return data.remove();
        /**
         * * 元素伤害发起者 的 战斗属性面板
         */
        const self_data = power_property.MergeProperty(power_property.GetProperty(self), this.annex.hurt_data ?? {});
        /**
         * * 获取 目标属性
         */
        const target_data = power_property.GetProperty(target);
        /**
         * * 定义 伤害参数
         */
        const options = this.DamageOptions(self);
        /**
         * * 执行 元素反应 并获得 伤害倍率 提升值
         */
        const fusion = RuneElementalReactions(target, target_data.add_rune, self_data.self_rune);
        /**
         * * 获取 增加 伤害倍率 后的 属性面板
         */
        const improve = power_property.MergeProperty(self_data, { double_damage: fusion + self_data.double_damage });
        /**
         * * 计算 伤害值
         */
        const damage = RuneElementalDamage(improve, erupt);
        // 触发 完全命中 的 伤害
        if (self_data.self_rune !== target_data.self_rune) target.applyDamage(damage, options);
        // 触发 元素抗性 的 伤害
        else ElementalResistance(target, options, damage, data_entity.rune_resistance);
        // 为目标 附加 元素印记
        power_property.SetProperty(target, { add_rune: self_data.self_rune });
        // 重置 自身属性
        power_property.SetProperty(self, data_entity.reset_battle_data);
        // 刷新 元素印记
        RuneClingEntity.CreateToken(target);
        // 返回 运行终止
        return data.remove();
    };
    /**
     * * 简单的创建器
     * 
     * @param {server.Entity} self - 发起元素攻击的实体
     * 
     * @param {server.Entity} target - 遭受攻击的目标实体
     * 
     * @param {boolean} erupt - 是否暴击
     * 
     * @param {data_format.GET_PROPERTY_PANEL} hurt_data - 攻击属性面板
     * 
     * @returns {PLAN} - 构建 的 元素攻击 计划表
     */
    static BriefCreate(self: server.Entity, target: server.Entity, erupt: boolean = false, hurt_data?: data_format.GET_PROPERTY_PANEL): PLAN {
        return this.Create(self.typeId, 1, { hurt_data, self, target, erupt });
    };
};
/**
 * * 符文依附于目标实体
 */
class RuneClingEntity extends Template {
    /**
     * * 缓存 符文令牌
     */
    static tokenGroup: RUNE_CLING_ENTITY[] = [];
    /**
     * * 检查 事件令牌 是否可用
     * 
     * @param {server.Entity} source 申请 执行操作 的 实体
     * 
     * @returns {boolean} 是否已经拥有符文令牌
     */
    static CreateToken(source: server.Entity): boolean {
        /**
         * * 检查 令牌 是否存在
         */
        const test = RuneClingEntity.tokenGroup.filter(token => token.id === source.id).length;
        // 如果 令牌 不存在 则 创建令牌
        if (test === 0) RuneClingEntity.tokenGroup.push({ id: source.id, wait: 100 });
        // 否则 更新 令牌
        else {
            RuneClingEntity.tokenGroup = RuneClingEntity.tokenGroup.filter(token => token.id !== source.id);
            RuneClingEntity.tokenGroup.push({ id: source.id, wait: 100 });
        }
        return test === 0;
    };
    public afterPlanEvent() {
        /**
         * * 获取 无效的 令牌
         */
        const reset = RuneClingEntity.tokenGroup.filter(token => token.wait <= 0);
        /**
         * * 获取 有效的 令牌
         */
        const valid = RuneClingEntity.tokenGroup.filter(token => token.wait > 0);
        // 移除 无效的 令牌
        RuneClingEntity.tokenGroup = RuneClingEntity.tokenGroup.filter(token => token.wait > 0);
        // 减少 令牌有效时间
        RuneClingEntity.tokenGroup.forEach(token => token.wait -= 1);
        // 令牌移除事件
        reset.forEach(
            token => {
                /**
                 * * 获取 实体
                 */
                const entity = server.world.getEntity(token.id);
                if (!entity) return;
                power_property.AlterProperty(entity, { add_rune: 'rune_void' });
            }
        );
        // 令牌有效事件
        valid.forEach(
            token => {
                /**
                 * * 获取 实体
                 */
                const entity = server.world.getEntity(token.id);
                if (!entity) return;
                /**
                 * * 粒子参数
                 */
                const molang = new server.MolangVariableMap();
                /**
                 * * 获取 实体属性
                 */
                const state = power_property.GetProperty(entity);
                /**
                 * * 获取 世界维度
                 */
                const dimension = entity.dimension;
                /**
                 * * 获取 颜色
                 */
                const colour = data_story.getRuneColor(state.self_rune);
                // 设定 粒子参数
                molang.setFloat('variable.scale', 0.35);
                molang.setColorRGB('variable.color', colour);
                molang.setVector3('variable.offset', { x: 0, y: 1.5, z: 0 });
                // 显示 粒子特效
                native_tools.TrySpawnParticle(dimension, `scripts:fusion_${state.add_rune}`, entity.getHeadLocation(), molang);
            }
        )
    };
    /**
     * * 简短的构造器
     * 
     * @param nameTag - 名称
     */
    static BriefCreate(nameTag: string): PLAN {
        return this.Create(nameTag, 1, {});
    };
};
/**
 * * 控制事件触发频率
 */
class ControlEventTrigger extends Template {
    /**
     * * 缓存 许可令牌
     */
    static tokenGroup: CONTROL_EVENT_TRIGGER[] = [];
    /**
     * * 检查 事件令牌 是否可用
     *
     * @param {string} type 许可令牌 的 类型
     * 
     * @param {server.Entity} source 申请 执行操作 的 实体
     *  
     * @param {number} wait 当前事件类型 的 冷却时间
     * 
     * @returns {boolean} 是否许可 本次操作
     */
    static EntityToken(type: string, source: server.Entity, wait: number): boolean {
        /**
         * * 检查 令牌 是否存在
         */
        const test = ControlEventTrigger.tokenGroup.filter(token => token.type === type && token.id === source.id).length;
        // 如果 令牌 不存在 则 创建令牌
        if (test === 0) ControlEventTrigger.tokenGroup.push({ type, id: source.id, wait });
        return test === 0;
    };
    /**
     * * 检查 事件令牌 是否可用
     *
     * @param {string} type 许可令牌 的 类型
     * 
     * @param {server.Block} block 申请 执行操作 的 方块
     *  
     * @param {number} wait 当前事件类型 的 冷却时间
     * 
     * @returns {boolean} 是否许可 本次操作
     */
    static BlockToken(type: string, block: server.Block, wait: number): boolean {
        /**
         * * 获取 方块维度
         */
        const dimension = block.dimension.id.split(/:/);
        /**
         * * 获取 方块坐标映射 ID
         */
        const blockID = `${dimension[0]}.${block.x}.${block.y}.${block.z}.${dimension[1]}`;
        /**
         * * 检查 令牌 是否存在
         */
        const test = ControlEventTrigger.tokenGroup.filter(token => token.type === type && token.id === blockID).length;
        // 如果 令牌 不存在 则 创建令牌
        if (test === 0) ControlEventTrigger.tokenGroup.push({ type, id: blockID, wait });
        return test === 0;
    };
    public afterPlanEvent() {
        ControlEventTrigger.tokenGroup = ControlEventTrigger.tokenGroup.filter(token => token.wait > 0);
        ControlEventTrigger.tokenGroup.forEach(token => token.wait -= 1);
    };
    /**
     * * 简短的构造器
     * 
     * @param nameTag - 名称
     */
    static BriefCreate(nameTag: string): PLAN {
        return this.Create(nameTag, 1, {});
    };
};
/**
 * * 水花粒子效果触发器
 */
class SprayParticleTrigger extends Template {
    public afterPlanEvent(data: AFTER_PLAN_DATA) {
        /**
         * * 获取 维度
         */
        const dimension = (this.annex.dimension_group ?? [server.world.getAllPlayers()[0].dimension])[0];
        /**
         * * 获取 位置
         */
        const location = (this.annex.location_group ?? [server.world.getAllPlayers()[0].location])[0];
        /**
         * * 获取 大小
         */
        const size = this.annex.index ?? 1;
        /**
         * * 参数数组
         */
        const molangGroup: server.MolangVariableMap[] = [
            new server.MolangVariableMap(),
            new server.MolangVariableMap(),
            new server.MolangVariableMap(),
            new server.MolangVariableMap()
        ];
        // 设置 参数
        molangGroup[0].setSpeedAndDirection('variable.property', size, math_tools.Vector.CONSTANT_EAST);
        molangGroup[1].setSpeedAndDirection('variable.property', size, math_tools.Vector.CONSTANT_WEST);
        molangGroup[2].setSpeedAndDirection('variable.property', size, math_tools.Vector.CONSTANT_SOUTH);
        molangGroup[3].setSpeedAndDirection('variable.property', size, math_tools.Vector.CONSTANT_NORTH);
        // 输出 粒子
        server.system.runTimeout(
            () => {
                native_tools.TrySpawnParticle(dimension, 'scripts:spray_main_ripple', location, molangGroup[0]);
                molangGroup.forEach(map => native_tools.TrySpawnParticle(dimension, 'scripts:spray_large_phase1', location, map));
            }, 1
        );
        server.system.runTimeout(
            () => {
                native_tools.TrySpawnParticle(dimension, 'scripts:spray_splash', location, molangGroup[0]);
                native_tools.TrySpawnParticle(dimension, 'scripts:spray_extend_ripple', location, molangGroup[0]);
                molangGroup.forEach(map => native_tools.TrySpawnParticle(dimension, 'scripts:spray_large_phase2', location, map));
            }, 6
        );
        return data.remove();
    };
    /**
     * * 简短的构造器
     * 
     * @param {string} nameTag - 计划表的识别名称
     * 
     * @param {server.Vector3} location - 计划表的创建位置
     * 
     * @param {server.Dimension} dimension - 计划表的执行维度
     * 
     * @returns {PLAN} - 构造的 符文 元素反应 计划表
     */
    static BriefCreate(nameTag: string, location: server.Vector3, dimension: server.Dimension): PLAN {
        return this.Create(nameTag, 1, { location_group: [location], dimension_group: [dimension] });
    }
};
/**
 * * 执行 符文元素反应
 * 
 * @param {server.Entity} target - 进行元素反应 的 目标实体
 * 
 * @param {data_format.RUNE_TYPE} old - 当前的 元素符文类型
 * 
 * @param {data_format.RUNE_TYPE} add - 新增的 元素符文类型
 * 
 * @returns {number} 元素符文反应后 提供的 倍率倍率提升值
 */
function RuneElementalReactions(target: server.Entity, old: data_format.RUNE_TYPE, add: data_format.RUNE_TYPE): number {
    /**
     * * 获取 原有的 符文映射值
     */
    const getOldValue = data_format.RUNE_COUNT[old];
    /**
     * * 获取 新增的 符文映射值
     */
    const getAddValue = data_format.RUNE_COUNT[add];
    // 判断 目标实体是否存在
    if (!target) return 0;
    /**
     * * 获取 符文融合结果
     */
    const result = data_entity.rune_fusion.get(getOldValue + getAddValue);
    // 判断 实体是否存在
    if (!result) return 0;
    // 执行 符文融合 事件
    if (result.event) result.event(target);
    // 返回 倍率倍率
    return result.double;
};
/**
 * * 结算当目标具有元素抗性时的伤害
 * 
 * @param {server.Entity} target - 目标实体
 * 
 * @param {server.EntityApplyDamageOptions} options - 伤害参数
 * 
 * @param {Number} damage - 伤害值
 * 
 * @param {Number} resistance - 元素抗性
 */
function ElementalResistance(target: server.Entity, options: server.EntityApplyDamageOptions, damage: number, resistance: number) {
    // 播放 粒子效果
    native_tools.TrySpawnParticle(target.dimension, 'constant:rune_resistance', target.getHeadLocation());
    // 播放 抵抗音效
    target.dimension.playSound('random.glass', target.location);
    // 伤害值 乘以 抗性
    target.applyDamage(damage * (1 - resistance), options);
};
/**
 * * 计算 符文元素伤害
 * 
 * @param {data_format.GET_PROPERTY_PANEL} data 用于 计算 的 属性面板
 * 
 * @param {boolean} erupt - 是否暴击
 * 
 * @returns {number} 计算后 的 伤害值
 */
function RuneElementalDamage(data: data_format.GET_PROPERTY_PANEL, erupt: boolean): number {
    /**
     * * 基础乘区
     */
    const baseValue = data.basic_attack + data.raise_basic_attack;
    /**
     * * 暴伤乘区
     */
    const eruptValue = (data.erupt_hurt + data.raise_erupt_hurt) / 100;
    /**
     * * 倍率乘区 - 未暴击
     */
    const eruptBefore = baseValue * data.double_damage;
    /**
     * * 倍率乘区 - 暴击后
     */
    const eruptAfter = (baseValue * eruptValue) * data.double_damage;
    // 返回 计算后 的 伤害值
    return erupt ? (eruptAfter + data.damage_increase) : (eruptBefore + data.damage_increase);
};
/**
 * * 计划表模板类所需的方法和属性
 */
interface PLAN {
    /**
     * 当前计划表的唯一数字标识符
     * 它用于在系统中追踪和识别特定的计划表实例
     */
    numberId: number;
    /**
     * 实例的时间刻积分
     * 这个分数通常用于计算计划表的优先级或者执行顺序
     */
    timeScore: number;
    /**
     * 用于在计划表执行前进行一些预处理
     * 它接收一个 BEFORE_PLAN_DATA 类型的参数, 包含了预处理所需的数据
     * 
     * @param data - 预处理所需的数据
     */
    beforePlanEvent(data: BEFORE_PLAN_DATA): void;
    /**
     * 用于在计划表执行后进行一些后处理
     * 它接收一个 AFTER_PLAN_DATA 类型的参数, 包含了后处理所需的数据
     * 
     * @param data - 后处理所需的数据
     */
    afterPlanEvent(data: AFTER_PLAN_DATA): void;
    /**
     * 计划表的类名
     * 类名用于标识不同的计划表类型, 并在系统中作为区分的依据
     */
    className: string;
    /**
     * 计划表重复执行的间隔时间
     * 冷却时间定义了计划表可以再次执行之前需要等待的时间
     */
    cooldown: number;
    /**
     * 计划表的附加参数
     * 附加参数提供了额外的配置选项, 这些选项可以用于自定义计划表的行为
     */
    annex: ANNEX_ARGS;
};
/**
 * * 计划表 的 前处理回调 数据接口
 */
interface BEFORE_PLAN_DATA {
    /**
     * 当前计划表的实例
     * 包含了计划表的所有属性和方法, 用于在预处理过程中访问和修改
     */
    plan: PLAN,
    /**
     * 移除当前计划表实例的回调函数
     * 调用此函数将从系统中移除当前计划表实例
     */
    remove: () => void,
    /**
     * 停止当前计划表实例的回调函数
     * 调用此函数将停止当前计划表实例的执行, 但可能不会从系统中移除
     */
    cease: () => void
};
/**
 * * 计划表 的 后处理回调 数据接口
 */
interface AFTER_PLAN_DATA {
    /**
     * 当前计划表的实例
     * 包含了计划表的所有属性和方法, 用于在后处理过程中访问和修改
     */
    plan: PLAN,
    /**
     * 移除当前计划表实例的回调函数
     * 调用此函数将从系统中移除当前计划表实例
     */
    remove: () => void,
};
/**
 * * 符文附着于实体 的 数据接口
 */
interface RUNE_CLING_ENTITY {
    /**
     * * 实体的唯一标识符
     */
    id: string;
    /**
     * * 元素符文的剩余附着事件
     */
    wait: number;
};
/**
 * * 控制事件触发器 的 数据接口
 */
interface CONTROL_EVENT_TRIGGER {
    /**
     * * 触发器的唯一标识
     */
    id: string;
    /**
     * * 触发器的类型
     */
    type: string;
    /**
     * * 触发器触发前的等待时间
     */
    wait: number;
};
/**
 * * 计划表附加参数 - 杂项
 */
interface ANNEX_ARGS {
    /**
     * * 目标方块
     */
    block?: server.Block;
    /**
     * * 索引
     */
    index?: number;
    /**
     * * 其他参数
     */
    other?: { [key: string]: any }
};
/**
 * * 计划表附加参数 - 战斗
 */
interface ANNEX_ARGS {
    /**
     * * 是否暴击
     */
    erupt?: boolean;
    /**
     * * 目标实体
     */
    target?: server.Entity | server.Player;
    /**
     * * 源头实体
     */
    self?: server.Entity | server.Player;
    /**
     * * 战斗属性数据
     */
    hurt_data?: data_format.GET_PROPERTY_PANEL;
    /**
     * * 伤害类型
     */
    hurt_Type?: keyof typeof server.EntityDamageCause;
};
/**
 * * 计划表附加参数 - 路径
 */
interface ANNEX_ARGS {
    /**
     * * 维度组
     */
    dimension_group?: server.Dimension[];
    /**
     * * 位置组
     */
    location_group?: server.Vector3[];
    /**
     * * 射击
     */
    shoot?: {
        /**
         * * 射击起始位置
         */
        start_place: server.Vector3,
        /**
         * * 射击方向
         */
        toward: server.Vector3,
        /**
         * * 射击距离
         */
        max_distance: number,
    };
};
/**
 * * 路径执行计划表 的 输入参数
 */
interface ROUTE_ARGS {
    /**
     * * 坐标组
     * 
     * @description 如果与< shoot >同时定义, 则 本参数失效
     */
    location_group: server.Vector3[];
    /**
     * * 运行维度
     */
    dimension: server.Dimension;
    /**
     * * 在运行时 执行的代码
     */
    on_move?: ROUTE_MOVE_CODE;
    /**
     * * 在结束时 执行的代码
     */
    on_done?: ROUTE_CLOSE_CODE;
    /**
     * * 粒子效果
     */
    particles: string[];
    /**
     * * 冷却时间
     */
    cooldown: number;
    /**
     * * 运行速度
     */
    speed: number;
    /**
     * * 坐标偏移
     */
    offset?: server.Vector3;
    /**
     * * 射击
     * 
     * @description 如果与< location_group >同时定义, 参数< location_group >失效
     */
    shoot?: {
        /**
         * * 射击起始位置
         */
        start_place: server.Vector3;
        /**
         * * 射击方向
         */
        toward: server.Vector3;
        /**
         * * 射击距离
         */
        max_distance: number;
    };
};
/**
 * * 路径执行计划表 运行时 的 回调函数
 * 
 * @returns {boolean} 容器是否继续运行
 */
interface ROUTE_MOVE_CODE {
    (data: data_format.ROUTE_ANNEX_ARGS): boolean;
};
/**
 * * 路径执行计划表 关闭时 的 回调函数
 */
interface ROUTE_CLOSE_CODE {
    (data: data_format.ROUTE_ANNEX_ARGS): void;
};